package com.puzzlebazar.client.admin;

import java.util.Iterator;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.puzzlebazar.client.main.view.ErrorMessages;
import com.puzzlebazar.client.user.UserModel;
import com.puzzlebazar.client.util.BasicFilter;
import com.puzzlebazar.client.util.Capabilities;
import com.puzzlebazar.client.util.ConcurrentVector;
import com.puzzlebazar.client.util.InstantChangeHandler;
import com.puzzlebazar.client.util.LabeledField;
import com.puzzlebazar.client.util.ValidatedField;

public class AdminGeneralView extends Composite {

	private final VerticalPanel mainPanel = new VerticalPanel();

	private final Grid grid = new Grid();
	private final LabeledField siteNameField;
	private final LabeledField siteDescriptionField;
	private final TextArea welcomeMessage = new TextArea();

	private final Button applyButton = new Button("Apply modifications");
	private final Button cancelButton = new Button("Cancel");
	private final Button resetButton = new Button("Reset database");

	private final MyChangeHandler changeHandler = new MyChangeHandler();

	private final ConcurrentVector<SettingsCallback> callbacks = 
		new ConcurrentVector<SettingsCallback>();

	private SiteInfo currentSiteInfo;
	private boolean modified = false;
	
	/**
	 * Creates a view that displays general administration tasks and lets the 
	 * user perform some typical operations on it.
	 */
	public AdminGeneralView() {

		grid.setWidth( "100%" );
		grid.setCellPadding( 7 );
		grid.setCellSpacing( 3 );
		grid.resize( 2, 3 );		
		
		siteNameField = new ValidatedField( 
				"Site name:", 
				SiteInfo.MIN_SITE_NAME_LENGTH,
				SiteInfo.MAX_SITE_NAME_LENGTH,
				new BasicFilter(new char[]{' ','-','.'}),
				ErrorMessages.ERROR_SITE_NAME_EMPTY );

		siteDescriptionField = new ValidatedField( 
				"Site description:", 
				SiteInfo.MIN_SITE_DESCRIPTION_LENGTH,
				SiteInfo.MAX_SITE_DESCRIPTION_LENGTH,
				new BasicFilter(new char[]{' ','-','.'}),
				ErrorMessages.ERROR_SITE_DESCRIPTION_EMPTY );

		VerticalPanel messagePanel = new VerticalPanel();
		messagePanel.setWidth( "100%" );
		Label label = new Label( "Welcome message  (can use HTML):" );
		label.setStylePrimaryName( "entryLabel" );
		messagePanel.add( label );
		messagePanel.add( welcomeMessage );
		welcomeMessage.setWidth( "100%" );
		welcomeMessage.setHeight( "70px" );
				
		
		siteNameField.addChangeHandler( changeHandler );
		siteDescriptionField.addChangeHandler( changeHandler );
		changeHandler.addToWidget( welcomeMessage );

		siteNameField.addToGrid( grid, 0 );
		siteDescriptionField.addToGrid( grid, 1 );
		grid.getColumnFormatter().setWidth(0, "130px");
		grid.getColumnFormatter().setWidth(1, "400px");
		
		applyButton.setWidth( "170px" );		
		cancelButton.setWidth( "100px" );		
		resetButton.setWidth( "150px" );		

		checkApplyEnabled();

		applyButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				SiteInfo siteInfo = createSiteInfo();
				if( siteInfo == null ) return;
				executeApply( siteInfo );
			}
		});	

		cancelButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				executeCancel();
			}
		});		

		resetButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if( !Window.confirm( "Really reset the database? All information will be lost, this action cannot be undone!" ) ) 
					return;
				executeReset();
			}
		});	

		final HorizontalPanel buttons = new HorizontalPanel();
		buttons.setWidth( "440px" );
		buttons.add(applyButton);
		buttons.add(cancelButton);
		if( Capabilities.canResetDatabase( UserModel.get().getUserInfo() ) )
			buttons.add(resetButton);
		
		
		mainPanel.setWidth( "100%" );
		mainPanel.setStylePrimaryName( "mainContent" );		
		mainPanel.add( grid );
		mainPanel.add( messagePanel );
		mainPanel.add( buttons );
		
		initWidget(mainPanel);
	}

	/**
	 * Clears all the states of the dialog box so that it can be reused
	 */
	public void resetState() {
		currentSiteInfo = SiteModel.get().getSiteInfo();
		modified = false;
		
		siteNameField.setEntry( currentSiteInfo.getSiteName() );
		siteDescriptionField.setEntry( currentSiteInfo.getSiteDescription() );
		welcomeMessage.setText( currentSiteInfo.getWelcomeMessage() );		
		DeferredCommand.addCommand( new Command() {
			@Override
			public void execute() {
				siteNameField.setFocus( true );
			}
		});

		checkApplyEnabled();		
	}

	/**
	 * Creates the site info based on the content of the text boxes
	 * 
	 * @return The site info, or null if some text boxes are invalid
	 */
	private SiteInfo createSiteInfo() {

		if( !checkApplyEnabled() ) return null;
		
		SiteInfo siteInfo = new SiteInfo();
		siteInfo.setSiteName( siteNameField.getEntry() );
		siteInfo.setSiteDescription( siteDescriptionField.getEntry() );
		siteInfo.setWelcomeMessage( welcomeMessage.getText() );
		
		return siteInfo;
	}

	/**
	 * Checks if the apply button should be enabled
	 * 
	 * @return True if the status of the fields is ok, false otherwise
	 */
	private boolean checkApplyEnabled() {

		boolean hasError = 
			siteNameField.hasError() || 
			siteDescriptionField.hasError();
		
		boolean isSame = true;
		
		if( currentSiteInfo != null ) {
			isSame = siteNameField.getEntry().equals( currentSiteInfo.getSiteName() ) &&
				siteDescriptionField.getEntry().equals( currentSiteInfo.getSiteDescription() ) &&
				welcomeMessage.getText().equals( currentSiteInfo.getWelcomeMessage() );
			if( isSame )
				executeModify( false );
		}
		
		applyButton.setEnabled( !hasError && !isSame );
		
		return !hasError && !isSame;
	}


	/**
	 * Adds a callback to be executed when the user logins
	 * 
	 * @param callback The callback to add
	 */
	public void addCallback( final SettingsCallback callback ) {
		callbacks.add( callback );			
	}

	/**
	 * Remove a callback added with addCallback()
	 * 
	 * @param callback The callback to remove
	 */
	public void removeCallback( SettingsCallback callback ) {
		callbacks.remove( callback );		
	}
	
	public interface SettingsCallback {

		/**
		 * Called when modification status changes
		 */
		public void modified( boolean modification );
		
		
		/**
		 * Called whenever the apply button is clicked
		 * 
		 * @param siteInfo The information for this site
		 */
		public void apply( SiteInfo siteInfo );
		
		/**
		 * Called whenever the cancel button is clicked
		 */
		public void cancel();

		/**
		 * Called whenever the reset database button is clicked
		 */
		public void reset();
	}	


	private class MyChangeHandler extends InstantChangeHandler {
		@Override
		public void onChange(ChangeEvent event) {
			executeModify( true );
			checkApplyEnabled();
		}			
	}	

	/**
	 * Execute all callbacks associated with a modification of a field
	 *
	 * @param isDifferent True if the content of the boxes is different from the original
	 */
	private void executeModify( boolean isDifferent ) {
		if( !isDifferent )
			modified = false;
		if( modified ) return;
		modified = isDifferent;
		for (Iterator<SettingsCallback> iterator = callbacks.iterator(); iterator.hasNext();) {
			SettingsCallback callback = iterator.next();
			callback.modified(modified);			
		}		

	}

	/**
	 * Execute all callbacks associated with the apply button 
	 *
	 * @param siteInfo The information for this site
	 */
	private void executeApply(SiteInfo siteInfo) {
		
		for (Iterator<SettingsCallback> iterator = callbacks.iterator(); iterator.hasNext();) {
			SettingsCallback callback = iterator.next();
			callback.apply( siteInfo );			
		}		

	}

	/**
	 * Execute all callbacks associated with the Reset button 
	 */
	private void executeReset() {
		
		for (Iterator<SettingsCallback> iterator = callbacks.iterator(); iterator.hasNext();) {
			SettingsCallback callback = iterator.next();
			callback.reset();			
		}		

	}
	
	/**
	 * Execute all callbacks associated with the Cancel button 
	 */
	private void executeCancel() {
		
		for (Iterator<SettingsCallback> iterator = callbacks.iterator(); iterator.hasNext();) {
			SettingsCallback callback = iterator.next();
			callback.cancel();			
		}		

	}
	
}
